<template>
	<xl-popup
		ref="popup-calendar"
		v-model="show"
		direction="bottom"
		:padding="[0, 10]"
		:borderRadius="borderRadius"
		:modalClose="modalClose"
		:zIndex="999"
	>
		<view class="xl-calendar">
			<view class="xl-calendar-header">
				<view v-if="showTitle" class="xl-calendar-header__title">
					{{ title }}
					<xl-icon class="xl-calendar-header__title__close" name="xl-close" size="18" color="#333" @click="close"></xl-icon>
				</view>
				<view class="xl-calendar-header__subtitle">
					<xl-icon v-if="mode == 'single'" name="xl-left" size="16" color="#555" @click="preMonth" right="15"></xl-icon>
					<view class="name">{{ subtitle }}</view>
					<xl-icon v-if="mode == 'single'" name="xl-right" size="16" color="#555" @click="nextMonth" left="15"></xl-icon>
				</view>
				<view class="xl-calendar-header__weekdays">
					<view class="xl-calendar-header__weekdays__weekday" v-for="(weak,index) in weekDays" :key="index">{{weak}}</view>
				</view>
			</view>
			<scroll-view :style="{height: $xl.calcUnit(scrollHeight)}"
				scroll-y
				@scroll="onScroll"
				:scrollTop="scrollTop"
				@touchmove.stop
			>
				<view class="xl-calendar-month-wrapper">
					<view v-for="(item, index) in months" :key="index">
						<view v-if="mode != 'single'" class="xl-calendar-month__title">{{ item.year }}年{{ item.month }}月</view>
						<view class="xl-calendar-month__days">
							<view v-if="showMark" class="month-mark">
								<text class="month-mark__text">{{ item.month }}</text>
							</view>
							<view class="month-day" v-for="(date, index1) in item.dates" :key="index1"
								:style="{height: $xl.calcUnit(rowHeight), padding: mode == 'range' ? '0px':'2px','--range-color': rangeColor}"
								@tap="clickHandler(date)"
							>
								<view class="month-day__box" :class="{'selected': selectClass(date), 'range-selected': rangeClass(date), 'range-start': date.timestamp == startTimestamp, 'range-end': date.timestamp == endTimestamp}">
									<text v-if="date.range" class="top-info">{{ date.range }}</text>
									<text class="day-info" :class="[date.disabled && 'day-info--disabled']">{{ date.day }}</text>
									<text v-if="date.lunar" class="bottom-info" :class="[date.disabled && 'bottom-info--disabled']">{{ date.lunar }}</text>
								</view>
							</view>
						</view>
					</view>
				</view>
			</scroll-view>
			<slot name="footer" v-if="showConfirm">
				<view class="xl-calendar__confirm">
					<xl-button
						shape="circle"
						:text="buttonDisabled ? confirmDisabledText : confirmText"
						:color="color"
						@click="confirm"
						:disabled="buttonDisabled"
					></xl-button>
				</view>
			</slot>
		</view>
	</xl-popup>
</template>

<script setup>
	import Calendar from './calendar.js'
	import { ref, computed, watch, onMounted, nextTick } from 'vue'
	import propsData from './props.js'
	const props = defineProps(propsData)
	const emits = defineEmits(['update:modelValue', 'confirm', 'close'])
	
	// 今天的日期
	let today = new Date()
	let todayTimestamp = ref(0)
	// 周几列表
	const weekDays = ref(["日", "一", "二", "三", "四", "五", "六"])
	// 月份滚动区域的高度
	const scrollHeight = computed(()=>{ return props.rowHeight * 5 + (props.mode == 'single' ? 10 : 45) })
	// 滚动至当前月份
	const scrollTop = ref(0)
	// 需要显示的月份的数组
	const months = ref([])
	// month组件中选择的日期数组
	const selected = ref([])
	// range范围模式，开始的Timestamp、结束的Timestamp
	const startTimestamp = ref(null)
	const endTimestamp = ref(null)
	const rangeColor = ref(props.color)
	
	
	// 双向绑定值，让v-model绑定的值双向变化
	const show = computed({
		get:function(){
			return props.modelValue
		},
		set:function(value){
			emits('update:modelValue', value);
		}
	})
	// show开启时，跳转至当前月份
	watch( () => props.modelValue, (newValue, oldValue) => {
		if(newValue){
			nextTick(() => {
				scrollTop.value = scrollHeight.value * props.preMonthNum || 0;
			})
		}
	},{ immediate: true })
	// 点击关闭
	function close() {
		show.value = false
		emits('close')
	}
	
	onMounted(() => {
		// 校验maxDate，不能小于minDate
		if (innerMaxDate.value && innerMinDate.value && innerMaxDate.value < innerMinDate.value) {
			return uni.$xl.error('maxDate不能小于minDate')
		}
		rangeColor.value = uni.$xl.color.colorToRgba(props.color, 0.9);
	})
	
	// 由于maxDate和minDate可以为字符串(2021-10-10)，或者数值(时间戳)，但是moment如果接受字符串形式的时间戳会有问题，这里进行处理
	const innerMaxDate = computed(()=>{
		return uni.$xl.validate.number(props.maxDate) ? Number(props.maxDate) : new Date(props.maxDate).getTime()
	})
	const innerMinDate = computed(()=>{
		return uni.$xl.validate.number(props.minDate) ? Number(props.minDate) : new Date(props.minDate).getTime()
	})
	// 多个条件的变化，会引起选中日期的变化，这里统一管理监听，执行一次的initMonths
	const selectedChange = computed(()=>{
		return [innerMinDate.value, innerMaxDate.value, props.defaultDate]
	})
	// selectedChange.value一开始为空，初始化时会自动赋值会触发执行一次initMonths，所以不用监听props.modelValue了
	watch( () => selectedChange.value, (newValue, oldValue) => {
		initMonths()
	},{ immediate: true })
	
	
	// 初始化月份数据
	function initMonths(){
		// 默认今天的日期
		today = new Date()
		todayTimestamp.value = new Date(today.getFullYear(), today.getMonth(), today.getDay()).getTime()
		months.value = []
		if(props.defaultDate){
			if(props.mode == 'single'){
				let initDate = new Date(Array.isArray(props.defaultDate) ? props.defaultDate[0] : props.defaultDate)
				selected.value = [uni.$xl.moment(initDate).format('YYYY-MM-DD')]
				setMonths(initDate.getFullYear(), initDate.getMonth()+1)
			}else if(props.mode == 'multiple' && Array.isArray(props.defaultDate)){
				let initDate = new Date(props.defaultDate[0])
				selected.value = props.defaultDate.map(item => uni.$xl.moment(item).format('YYYY-MM-DD'))
				setMonths(initDate.getFullYear(), initDate.getMonth()+1)
			}else if(props.mode == 'range' && Array.isArray(props.defaultDate) && props.defaultDate.length == 2){
				let startDate = new Date(props.defaultDate[0])
				let endDate = new Date(props.defaultDate[1])
				selected.value = [uni.$xl.moment(startDate).format('YYYY-MM-DD'), uni.$xl.moment(endDate).format('YYYY-MM-DD')]
				startTimestamp.value = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate()).getTime()
				endTimestamp.value = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()).getTime()
				setMonths(startDate.getFullYear(), startDate.getMonth()+1)
			}else{
				setMonths(today.getFullYear(), today.getMonth() + 1)
			}
		}else{
			// 没有默认值，则显示今天的日历
			setMonths(today.getFullYear(), today.getMonth() + 1)
		}
	}
	
	// 初始化所有月份months
	function setMonths(year, month){
		// 添加当前的月份
		months.value.push(setMonth(year, month))
		// 多个日期和范围日期需要多个月份
		if(props.mode == 'multiple' || props.mode == 'range'){
			// 添加前面的月份
			let preYear = year
			let preMonth = month
			for(let index = 1; index <= props.preMonthNum; index++){
			    preMonth = preMonth - 1
				if(preMonth == 0){
					preMonth = 12
					preYear -= 1
				}
				months.value.splice(0,0,setMonth(preYear, preMonth))
			}
			// 添加后面的月份
			let affterYear = year
			for(let index = 1; index <= props.nextMonthNum; index ++){
				let afferMonth = (month + index) % 12 == 0 ? 12 : (month + index) % 12 
				if(afferMonth == 1) affterYear += 1
				months.value.push(setMonth(affterYear, afferMonth))
			}
		}
	}
	// 设置单个月份的数据
	function setMonth(year, month) {
		// 获取当前月份的天数
		let days = new Date(year, month, 0).getDate()
		let dates = []
		for (let day = 1; day <= days; day++) {
			let date = new Date(year, month - 1, day);
			let timestamp = date.getTime()
			dates.push({
				day: day,
				month: month,
				year: year,
				weak: date.getDay(),
				lunar: props.showLunar ? Calendar.solar2lunar(year, month, day).IDayCn : '',
				disabled: (innerMinDate.value != 0 && timestamp < innerMinDate.value) || (innerMaxDate.value != 0 && timestamp > innerMaxDate.value),
				range: '',
				timestamp: timestamp,
				date: year + '-' + (month>9 ? month: '0'+ month) + '-' + (day>9 ? day: '0'+ day)
			})
		}
		// 添加一周前面空的内容
		for (let begin = dates[0].weak - 1; begin >= 0; begin--) {
			dates.splice(0, 0, {
				day: "",
				month: month,
				year: year,
				weak: begin,
				disabled: true,
				timestamp: -1
			})
		}
		return {
			dates: dates,
			month: month,
			year : year,
			time: year + "年" + month + "月"
		}
	}
	
	// 单个日期点击上一个月份
	function preMonth(){
		let preYear = months.value[0].year
		let preMonth = months.value[0].month - 1
		if(preMonth == 0){
			preMonth = 12
			preYear -= 1
		}
		months.value = [setMonth(preYear, preMonth)]
	}
	// 单个日期点击下一个月份
	function nextMonth(){
		let affterYear = months.value[0].year
		let afferMonth = months.value[0].month + 1
		if(afferMonth == 13){
			afferMonth = 1
			affterYear += 1
		}
		months.value = [setMonth(affterYear, afferMonth)]
	}
	
	// 点击某一个日期
	function clickHandler(clickItem) {
		if (props.readonly || clickItem.disabled) return;
		if (props.mode === 'single') {
			// 单选情况下，让数组中的元素为当前点击的日期
			selected.value = [clickItem.date]
			emits('confirm', selected.value)
		} else if (props.mode === 'multiple') {
			if (selected.value.includes(clickItem.date)) {
				// 如果点击的日期已在数组中，则进行移除操作，也就是达到反选的效果
				selected.value = selected.value.filter(item => item !== clickItem.date);
			} else if (selected.value.length < props.maxCount){
				selected.value.push(clickItem.date)
			} else if (selected.value.length >= props.maxCount){
				uni.$xl.toast(`选择天数不能超过 ${props.maxCount} 天`)
			}
		} else {
			// 选择区间形式
			if (selected.value.length === 0 || selected.value.length >= 2) {
				// 如果原来就为0或者大于2的长度，则当前点击的日期，就是开始日期
				selected.value = [clickItem.date]
				startTimestamp.value = clickItem.timestamp
				endTimestamp.value = null
			} else if (selected.value.length === 1) {
				// 如果已经选择了开始日期
				// 如果当前选择的日期小于上一次选择的日期，则当前的日期定为开始日期
				if (clickItem.timestamp < startTimestamp.value) {
					selected.value = [clickItem.date]
					startTimestamp.value = clickItem.timestamp
				} else if (clickItem.timestamp > startTimestamp.value) {
					// 当前日期减去最大可选的日期天数，如果大于起始时间，则进行提示
					if((clickItem.timestamp - startTimestamp.value) / (24 * 60 * 60 * 1000) > props.maxRange) {
						if(props.rangePrompt) {
							uni.$xl.toast(props.rangePrompt)
						} else {
							uni.$xl.toast(`选择天数不能超过 ${props.maxRange} 天`)
						}
						return
					}
					// 如果当前日期大于已有日期，将当前的添加到数组尾部
					selected.value.push(clickItem.date)
					endTimestamp.value = clickItem.timestamp
				} else {
					// 选择区间时，只有一个日期的情况下，且不允许选择起止为同一天的话，不允许选择自己
					if (!props.allowSameDay) return
					selected.value.push(clickItem.date)
					endTimestamp.value = clickItem.timestamp
				}
			}
		}
	}
	
	// 计算选中样式
	function selectClass(item){
		if (selected.value.includes(item.date)){
			if(item.timestamp == startTimestamp.value && item.timestamp == endTimestamp.value) item.range = props.startText + "/" + props.endText;
			else if (item.timestamp == startTimestamp.value) item.range = props.startText;
			else if (item.timestamp == endTimestamp.value) item.range = props.endText;
			return true
		} else {
			item.range = "";
			return false
		}
	}
	// 计算范围样式
	function rangeClass(item){
		if (startTimestamp.value && endTimestamp.value && item.timestamp > startTimestamp.value && item.timestamp < endTimestamp.value) return true
		else return false
	}
	
	// 确定按钮是否禁用，当range类型不足2个时禁用
	const buttonDisabled = computed(()=>{
		// 如果为range类型，且选择的日期个数不足1个时，让底部的按钮出于disabled状态
		if (props.mode === 'range') {
			if (selected.value.length <= 1) {
				return true
			} else {
				return false
			}
		} else {
			return false
		}
	})
	// 点击确定按钮
	function confirm() {
		if (!buttonDisabled.value) {
			emits('confirm', selected.value)
			show.value = false
		}
	}
	
	// 在月份滚动区域中，当前视图中月份的index索引
	let monthIndex = ref(0)
	const subtitle = computed(()=>{
		// 初始化时，this.months为空数组，所以需要特别判断处理
		if (months.value.length) {
			return `${months.value[monthIndex.value].year}年${months.value[monthIndex.value].month}月`
		} else {
			return ''
		}
	})

	// scroll-view滚动监听
	function onScroll(event) {
		// 不允许小于0的滚动值，如果scroll-view到顶了，继续下拉，会出现负数值
		const scrollTop = Math.max(0, event.detail.scrollTop)
		// 将当前滚动条数值，除以滚动区域的高度，可以得出当前滚动到了哪一个月份的索引
		monthIndex.value = Math.floor((scrollTop + 20) / scrollHeight.value)
	}
</script>

<style lang="scss" scoped>

.xl-calendar {
	&-header {
		padding-bottom: 4px;
		border-bottom: 1rpx solid #dadbde;
	
		&__title {
			font-size: 16px;
			color: #333;
			text-align: center;
			height: 42px;
			line-height: 42px;
			font-weight: bold;
			position: relative;
			&__close{
				position: absolute;
				top: 0rpx;
				right: 10rpx;
			}
		}
	
		&__subtitle {
			height: 40px;
			font-weight: bold;
			/* #ifndef APP-NVUE */
			display: flex;
			/* #endif */
			flex-direction: row;
			justify-content: center;
			align-items: center;
			.name{
				font-size: 16px;
				color: #333;
			}
		}
	
		&__weekdays {
			/* #ifndef APP-NVUE */
			display: flex;
			/* #endif */
			flex-direction: row;
			justify-content: space-between;
	
			&__weekday {
				font-size: 13px;
				color: #333;
				line-height: 30px;
				flex: 1;
				text-align: center;
			}
		}
	}
	&__confirm {
		padding: 7px 15px;
	}
	.xl-calendar-month-wrapper {
		.xl-calendar-month {
			&__title {
				font-size: 14px;
				line-height: 50px;
				height: 42px;
				color: #333;
				text-align: center;
				font-weight: bold;
			}
		
			&__days {
				position: relative;
				/* #ifndef APP-NVUE */
				display: flex;
				/* #endif */
				flex-direction: row;
				flex-wrap: wrap;
		
				.month-mark {
					position: absolute;
					top: 0;
					bottom: 0;
					left: 0;
					right: 0;
					/* #ifndef APP-NVUE */
					display: flex;
					/* #endif */
					flex-direction: row;
					justify-content: center;
					align-items: center;
					z-index: 99;
		
					&__text {
						font-size: 155px;
						color: rgba(231, 232, 234, 0.83);
					}
				}
		
				.month-day {
					/* #ifndef APP-NVUE */
					display: flex;
					/* #endif */
					flex-direction: row;
					// vue下使用css进行宽度计算，因为某些安卓机会无法进行js获取父元素宽度进行计算得出，会有偏移
					width: calc(100% / 7);
					box-sizing: border-box;
					position: relative;
					z-index: 999;
		
					&__box {
						flex: 1;
						/* #ifndef APP-NVUE */
						display: flex;
						/* #endif */
						flex-direction: row;
						align-items: center;
						justify-content: center;
						position: relative;
						
						&.selected {
							background-color: v-bind(color);
							border-radius: 4px;
							.day-info {
								color: #ffffff;
							}
							.bottom-info {
								color: #ffffff;
							}
						}
						&.range-selected {
							background-color: var(--range-color);
							opacity: 0.3;
							border-radius: 0;
							.day-info {
								color: #ffffff;
							}
							.bottom-info {
								color: #ffffff;
							}
						}
								
						&.range-start {
							border-top-right-radius: 0;
							border-bottom-right-radius: 0;
							&.range-end{
								border-radius: 4px;
							}
						}
								
						&.range-end {
							border-top-left-radius: 0;
							border-bottom-left-radius: 0;
						}
						
						.top-info {
							color: #ffffff;
							text-align: center;
							position: absolute;
							top: 4px;
							font-size: 10px;
							text-align: center;
							left: 0;
							right: 0;
						}
						
						.day-info {
							text-align: center;
							font-size: 16px;
							&--disabled {
								color: #cacbcd;
							}
						}
						
						.bottom-info {
							color: #606266;
							text-align: center;
							position: absolute;
							bottom: 4px;
							font-size: 10px;
							text-align: center;
							left: 0;
							right: 0;
							&--disabled {
								color: #cacbcd;
							}
						}
		
						&__dot {
							width: 7px;
							height: 7px;
							border-radius: 100px;
							background-color: #f56c6c;
							position: absolute;
							top: 12px;
							right: 7px;
						}
					}
				}
			}
		}
	}
}

	
</style>
